const _ = require("lodash");
const swagger = require("swagger-client");
const compareVersions = require("compare-versions");

var SwaggerData, isOAS3;
function handlePath(path) {
	if (path === "/") return path;
	if (path.charAt(0) != "/") {
		path = "/" + path;
	}
	if (path.charAt(path.length - 1) === "/") {
		path = path.substr(0, path.length - 1);
	}
	return path;
}

function openapi2swagger(data) {
	data.swagger = "2.0";
	_.each(data.paths, apis => {
		_.each(apis, api => {
			_.each(api.responses, res => {
				if (
					res.content &&
					res.content["application/json"] &&
					typeof res.content["application/json"] === "object"
				) {
					Object.assign(res, res.content["application/json"]);
					delete res.content;
				}
				if (
					res.content &&
					res.content["application/hal+json"] &&
					typeof res.content["application/hal+json"] === "object"
				) {
					Object.assign(res, res.content["application/hal+json"]);
					delete res.content;
				}
				if (
					res.content &&
					res.content["*/*"] &&
					typeof res.content["*/*"] === "object"
				) {
					Object.assign(res, res.content["*/*"]);
					delete res.content;
				}
			});
			if (api.requestBody) {
				if (!api.parameters) api.parameters = [];
				let body = {
					type: "object",
					name: "body",
					in: "body"
				};
				try {
					body.schema = api.requestBody.content["application/json"].schema;
				} catch (e) {
					body.schema = {};
				}

				api.parameters.push(body);
			}
		});
	});

	return data;
}

async function handleSwaggerData(res) {
	return await new Promise(resolve => {
		let data = swagger({
			spec: res
		});

		data.then(res => {
			resolve(res.spec);
		});
	});
}

async function run(res) {
	let interfaceData = { apis: [], cats: [] };
	if (typeof res === "string" && res) {
		try {
			res = JSON.parse(res);
		} catch (e) {
			console.error("json 解析出错", e.message);
		}
	}

	isOAS3 = res.openapi && compareVersions(res.openapi, "3.0.0") >= 0;
	if (isOAS3) {
		res = openapi2swagger(res);
	}
	res = await handleSwaggerData(res);
	SwaggerData = res;

	interfaceData.basePath = res.basePath || "";

	if (res.tags && Array.isArray(res.tags)) {
		res.tags.forEach(tag => {
			interfaceData.cats.push({
				name: tag.name,
				desc: tag.description
			});
		});
	} else {
		res.tags = [];
	}

	_.each(res.paths, (apis, path) => {
		// parameters is common parameters, not a method
		delete apis.parameters;
		_.each(apis, (api, method) => {
			api.path = path;
			api.method = method;
			let data = null;
			try {
				data = handleSwagger(api, res.tags);
				if (data.catname) {
					if (!_.find(interfaceData.cats, item => item.name === data.catname)) {
						if (res.tags.length === 0) {
							interfaceData.cats.push({
								name: data.catname,
								desc: data.catname
							});
						}
					}
				}
			} catch (err) {
				data = null;
			}
			if (data) {
				interfaceData.apis.push(data);
			}
		});
	});

	interfaceData.cats = interfaceData.cats.filter(catData => {
		let catName = catData.name;
		return _.find(interfaceData.apis, apiData => {
			return apiData.catname === catName;
		});
	});

	return interfaceData;
}

function handleSwagger(data, originTags = []) {
	let api = {};
	//处理基本信息
	api.method = data.method.toUpperCase();
	api.title = data.summary || data.path;
	api.desc = data.description;
	api.catname = null;
	if (data.tags && Array.isArray(data.tags)) {
		api.tag = data.tags;
		for (let i = 0; i < data.tags.length; i++) {
			if (/v[0-9\.]+/.test(data.tags[i])) {
				continue;
			}

			// 如果根路径有 tags，使用根路径 tags,不使用每个接口定义的 tag 做完分类
			if (
				originTags.length > 0 &&
				_.find(originTags, item => {
					return item.name === data.tags[i];
				})
			) {
				api.catname = data.tags[i];
				break;
			}

			if (originTags.length === 0) {
				api.catname = data.tags[i];
				break;
			}
		}
	}

	api.path = handlePath(data.path);
	api.req_params = [];
	api.req_body_form = [];
	api.req_headers = [];
	api.req_query = [];
	api.req_body_type = "raw";
	api.res_body_type = "raw";

	if (data.produces && data.produces.indexOf("application/json") > -1) {
		api.res_body_type = "json";
		api.res_body_is_json_schema = true;
	}

	if (data.consumes && Array.isArray(data.consumes)) {
		if (
			data.consumes.indexOf("application/x-www-form-urlencoded") > -1 ||
			data.consumes.indexOf("multipart/form-data") > -1
		) {
			api.req_body_type = "form";
		} else if (data.consumes.indexOf("application/json") > -1) {
			api.req_body_type = "json";
			api.req_body_is_json_schema = true;
		}
	}

	//处理response
	api.res_body = handleResponse(data.responses);
	try {
		JSON.parse(api.res_body);
		api.res_body_type = "json";
		api.res_body_is_json_schema = true;
	} catch (e) {
		api.res_body_type = "raw";
	}
	//处理参数
	function simpleJsonPathParse(key, json) {
		if (
			!key ||
			typeof key !== "string" ||
			key.indexOf("#/") !== 0 ||
			key.length <= 2
		) {
			return null;
		}
		let keys = key.substr(2).split("/");
		keys = keys.filter(item => {
			return item;
		});
		for (let i = 0, l = keys.length; i < l; i++) {
			try {
				json = json[keys[i]];
			} catch (e) {
				json = "";
				break;
			}
		}
		return json;
	}

	if (data.parameters && Array.isArray(data.parameters)) {
		data.parameters.forEach(param => {
			if (param && typeof param === "object" && param.$ref) {
				param = simpleJsonPathParse(param.$ref, {
					parameters: SwaggerData.parameters
				});
			}
			let defaultParam = {
				name: param.name,
				value: (() => {
					if (param.type === "integer") {
						return number;
					} else {
						return param.type;
					}
				})(),
				example: String(param.enum || ""),
				desc: param.description,
				required: param.required ? "1" : "0"
			};

			if (param.in) {
				switch (param.in) {
					case "path":
						api.req_params.push(defaultParam);
						break;
					case "query":
						api.req_query.push(defaultParam);
						break;
					case "body":
						handleBodyPamras(param.schema, api);
						break;
					case "formData":
						defaultParam.type = param.type === "file" ? "file" : "text";
						if (param.example) {
							defaultParam.example = param.example;
						}
						api.req_body_form.push(defaultParam);
						break;
					case "header":
						api.req_headers.push(defaultParam);
						break;
				}
			} else {
				api.req_query.push(defaultParam);
			}
		});
	}

	return api;
}

function isJson(json) {
	try {
		return JSON.parse(json);
	} catch (e) {
		return false;
	}
}
function propertiyToArray(jsonData) {
	/* 将 properties对象 转为数组 方便编辑的时候固定顺序 */
	if (jsonData.type === "object" && _.isPlainObject(jsonData.properties)) {
		const _properties = _.map(jsonData.properties, (item, propname) => {
			item = propertiyToArray(item);
			return {
				...item,
				propname
			};
		});
		jsonData.properties = _properties;
	} else if (jsonData.type === "array") {
		jsonData.items = propertiyToArray(jsonData.items);
		jsonData.items.propname = jsonData.items.xml?.name || "";
	}
	return jsonData;
}
function handleBodyPamras(data, api) {
	/* 方便编辑 */
	/* $$ref 是已经转换过，$ref是还没有转换 */
	api.req_body_other = JSON.stringify(propertiyToArray(data), null, 2);
	if (isJson(api.req_body_other)) {
		api.req_body_type = "json";
		api.req_body_is_json_schema = true;
	}
}

function handleResponse(api) {
	let res_body = "";
	if (!api || typeof api !== "object") {
		return res_body;
	}
	let codes = Object.keys(api);
	let curCode;
	if (codes.length > 0) {
		if (codes.indexOf("200") > -1) {
			curCode = "200";
		} else curCode = codes[0];

		let res = api[curCode];
		if (res && typeof res === "object") {
			if (res.schema) {
				res_body = JSON.stringify(res.schema, null, 2);
			} else if (res.description) {
				res_body = res.description;
			}
		} else if (typeof res === "string") {
			res_body = res;
		} else {
			res_body = "";
		}
	} else {
		res_body = "";
	}
	return res_body;
}

module.exports = run;
